package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.HotArticleConstants;
import com.heima.common.constants.message.ArticleVisitStreamMess;
import com.heima.common.constants.message.UpdateArticleMess;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.seata.common.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateHotArticleJob {

    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    HotArticleService hotArticleService;


    @XxlJob("UpdateHotArticleJob")
    public ReturnT updateHotArticleHandler(String params){
        log.info("热文章分值更新 调度任务开始执行....");
        //todo 1 获取redis行为列表中待处理的数据
        List<UpdateArticleMess> articleMessList=getUpdateArticleMeses();
        if (CollectionUtils.isEmpty(articleMessList)){
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }
        //todo 2. 将数据按照文章分组  进行聚合统计 得到待更新的数据列表
        List<ArticleVisitStreamMess> waitUpdateScoreData =getArticleVisitStreamMesses(articleMessList);
        if (CollectionUtils.isEmpty(waitUpdateScoreData)) {
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }
        //todo 3 根据分组聚合处理的结果更新文章分值  更新缓存

        //todo 4 今日发布的文章整体热度 * 3

        // TODO 定时更新文章热度
        log.info("热文章分值更新 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    /**
     * 将数据按照文章分组  进行聚合统计 得到待更新的数据列表
     * @param articleMessList
     * @return
     */
    private List<ArticleVisitStreamMess> getArticleVisitStreamMesses(List<UpdateArticleMess> articleMessList) {
        List<ArticleVisitStreamMess> waitUpdateScoreData = new ArrayList<>();
        //1 分组 key:文章id value: 对应文章的行为列表
        Map<Long, List<UpdateArticleMess>> map =
                articleMessList.stream()
                        .collect(Collectors.groupingBy(
                                UpdateArticleMess::getArticleId));

        //2 聚合运算
        map.forEach((articleId,messList)->{
            Optional<ArticleVisitStreamMess> result = messList.stream().map(articleMes -> {
                ArticleVisitStreamMess articleVisitStreamMess = new ArticleVisitStreamMess();
                articleVisitStreamMess.setArticleId(articleId);

                switch (articleMes.getType()) {
                    case LIKES:
                        //todo 设置点赞数量
                        articleVisitStreamMess.setLike(articleMes.getAdd());
                        break;
                    case VIEWS:
                        //todo 涉资阅读量
                        articleVisitStreamMess.setView(articleMes.getAdd());
                        break;
                    case COMMENT:
                        //todo 设置品论数
                        articleVisitStreamMess.setComment(articleMes.getAdd());
                        break;
                    case COLLECTION:
                        //todo 设置收藏数量
                        articleVisitStreamMess.setCollect(articleMes.getAdd());
                        break;
                }
                //TODO =====================================
                return articleVisitStreamMess;

            }).reduce(new BinaryOperator<ArticleVisitStreamMess>() {
                @Override
                public ArticleVisitStreamMess apply(ArticleVisitStreamMess a1, ArticleVisitStreamMess a2) {
                    a1.setLike(a1.getLike() + a2.getLike());
                    a1.setView(a1.getView() + a2.getView());
                    a1.setComment(a1.getComment() + a2.getComment());
                    a1.setCollect(a1.getCollect() + a2.getCollect());
                    return a1;
                }
            });
            if(result.isPresent()){
                // 聚合结果
                ArticleVisitStreamMess visitStreamMess = result.get();
                log.info("热点文章 聚合计算结果  ===>{}" , visitStreamMess);
                waitUpdateScoreData.add(visitStreamMess);
            }
        });
        return waitUpdateScoreData;
    }

    /**
     * 获取redis行为列表中待处理的数据
     * @return
     */
    private List<UpdateArticleMess> getUpdateArticleMeses() {
        //todo 1. 获取redis 行为列表中待处理数据
        ListOperations listOperations = redisTemplate.opsForList();
        //todo 得到当前行为数据量
        Long size = listOperations.size(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);
        //todo 采用管道技术
        List result = redisTemplate.executePipelined(new RedisCallback<List<UpdateArticleMess>>() {

            @Override
            public List<UpdateArticleMess> doInRedis(RedisConnection redisConnection) throws DataAccessException {
                //todo 开启管道执行
                redisConnection.openPipeline();
                //获取所有集合数据
                redisConnection.lRange(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), 0, (size - 1));
                //todo 删除一处理的数据
                redisConnection.lTrim(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), size, -1);
                return null;
            }
        }, RedisSerializer.string());
        if (result.size()>0){
            List<String>  listData = (List<String>) result.get(0);
            return listData.stream()
                    .map(s -> JSON.parseObject(s,UpdateArticleMess.class))
                    .collect(Collectors.toList());
        }
        return null;
    }
}
